
#include "RenderList.h"
#include "3dDef.h"


//
int Compare_AvgZ_POLYF4DV1(const void *arg1, const void *arg2)
{
	// this function comapares the average z's of two polygons and is used by the
	// depth sort surface ordering algorithm

	float z1, z2;

	POLYF4DV1_PTR poly_1, poly_2;

	// dereference the poly pointers
	poly_1 = *((POLYF4DV1_PTR *)(arg1));
	poly_2 = *((POLYF4DV1_PTR *)(arg2));

	// compute z average of each polygon
	z1 = (float)0.33333*(poly_1->tvlist[0][Z] + poly_1->tvlist[1][Z] + poly_1->tvlist[2][Z]);

	// now polygon 2
	z2 = (float)0.33333*(poly_2->tvlist[0][Z] + poly_2->tvlist[1][Z] + poly_2->tvlist[2][Z]);

	// compare z1 and z2, such that polys' will be sorted in descending Z order
	if (z1 > z2)
		return(-1);
	else
		if (z1 < z2)
			return(1);
		else
			return(0);

} // end Compare_AvgZ_POLYF4DV1

////////////////////////////////////////////////////////////////////////////////

int Compare_NearZ_POLYF4DV1(const void *arg1, const void *arg2)
{
	// this function comapares the closest z's of two polygons and is used by the
	// depth sort surface ordering algorithm

	float z1, z2;

	POLYF4DV1_PTR poly_1, poly_2;

	// dereference the poly pointers
	poly_1 = *((POLYF4DV1_PTR *)(arg1));
	poly_2 = *((POLYF4DV1_PTR *)(arg2));

	// compute the near z of each polygon
	z1 = MIN(poly_1->tvlist[0][Z], poly_1->tvlist[1][Z]);
	z1 = MIN(z1, poly_1->tvlist[2][Z]);

	z2 = MIN(poly_2->tvlist[0][Z], poly_2->tvlist[1][Z]);
	z2 = MIN(z2, poly_2->tvlist[2][Z]);

	// compare z1 and z2, such that polys' will be sorted in descending Z order
	if (z1 > z2)
		return(-1);
	else
		if (z1 < z2)
			return(1);
		else
			return(0);

} // end Compare_NearZ_POLYF4DV1

////////////////////////////////////////////////////////////////////////////////

int Compare_FarZ_POLYF4DV1(const void *arg1, const void *arg2)
{
	// this function comapares the farthest z's of two polygons and is used by the
	// depth sort surface ordering algorithm

	float z1, z2;

	POLYF4DV1_PTR poly_1, poly_2;

	// dereference the poly pointers
	poly_1 = *((POLYF4DV1_PTR *)(arg1));
	poly_2 = *((POLYF4DV1_PTR *)(arg2));

	// compute the near z of each polygon
	z1 = MAX(poly_1->tvlist[0][Z], poly_1->tvlist[1][Z]);
	z1 = MAX(z1, poly_1->tvlist[2][Z]);

	z2 = MAX(poly_2->tvlist[0][Z], poly_2->tvlist[1][Z]);
	z2 = MAX(z2, poly_2->tvlist[2][Z]);

	// compare z1 and z2, such that polys' will be sorted in descending Z order
	if (z1 > z2)
		return(-1);
	else
		if (z1 < z2)
			return(1);
		else
			return(0);

} // end Compare_FarZ_POLYF4DV1
//////////////////////////////////////////////////////////////////////////////////////////////////



int CRenderList::Transform(	CMatrix44& mt,		// transformation matrix
							int coord_select)   // selects coords to transform
{
	// this function simply transforms all of the polygons vertices in the local or trans
	// array of the render list by the sent matrix

	// what coordinates should be transformed?
	switch(coord_select)
	{
	case TRANSFORM_LOCAL_ONLY:
		{
			for (int poly = 0; poly < num_polys; poly++)
			{
				// acquire current polygon
				POLYF4DV1_PTR curr_poly = poly_ptrs[poly];

				// is this polygon valid?
				// transform this polygon if and only if it's not clipped, not culled,
				// active, and visible, note however the concept of "backface" is 
				// irrelevant in a wire frame engine though
				if ((curr_poly==NULL) || !(curr_poly->state & POLY4DV1_STATE_ACTIVE) ||
					(curr_poly->state & POLY4DV1_STATE_CLIPPED ) ||
					(curr_poly->state & POLY4DV1_STATE_BACKFACE) )
					continue; // move onto next poly

				// all good, let's transform 
				for (int vertex = 0; vertex < 3; vertex++)
				{
					// transform the vertex by mt
					CPoint4d presult; // hold result of each transformation

					// transform point
					Mat_Mul_VECTOR4D_4X4(curr_poly->vlist[vertex], mt, presult);

					// store result back
					curr_poly->vlist[vertex] = presult;
				} // end for vertex

			} // end for poly

		} break;

	case TRANSFORM_TRANS_ONLY:
		{
			// transform each "transformed" vertex of the render list
			// remember, the idea of the tvlist[] array is to accumulate
			// transformations
			for (int poly = 0; poly < num_polys; poly++)
			{
				// acquire current polygon
				POLYF4DV1_PTR curr_poly = poly_ptrs[poly];

				// is this polygon valid?
				// transform this polygon if and only if it's not clipped, not culled,
				// active, and visible, note however the concept of "backface" is 
				// irrelevant in a wire frame engine though
				if ((curr_poly==NULL) || !(curr_poly->state & POLY4DV1_STATE_ACTIVE) ||
					(curr_poly->state & POLY4DV1_STATE_CLIPPED ) ||
					(curr_poly->state & POLY4DV1_STATE_BACKFACE) )
					continue; // move onto next poly

				// all good, let's transform 
				for (int vertex = 0; vertex < 3; vertex++)
				{
					// transform the vertex by mt
					CPoint4d presult; // hold result of each transformation

					// transform point
					Mat_Mul_VECTOR4D_4X4(curr_poly->tvlist[vertex], mt, presult);

					// store result back
					curr_poly->tvlist[vertex] = presult; 
				} // end for vertex

			} // end for poly

		} break;

	case TRANSFORM_LOCAL_TO_TRANS:
		{
			// transform each local/model vertex of the render list and store result
			// in "transformed" vertex list
			for (int poly = 0; poly < num_polys; poly++)
			{
				// acquire current polygon
				POLYF4DV1_PTR curr_poly = poly_ptrs[poly];

				// is this polygon valid?
				// transform this polygon if and only if it's not clipped, not culled,
				// active, and visible, note however the concept of "backface" is 
				// irrelevant in a wire frame engine though
				if ((curr_poly==NULL) || !(curr_poly->state & POLY4DV1_STATE_ACTIVE) ||
					(curr_poly->state & POLY4DV1_STATE_CLIPPED ) ||
					(curr_poly->state & POLY4DV1_STATE_BACKFACE) )
					continue; // move onto next poly

				// all good, let's transform 
				for (int vertex = 0; vertex < 3; vertex++)
				{
					// transform the vertex by mt
					Mat_Mul_VECTOR4D_4X4(curr_poly->vlist[vertex], mt, curr_poly->tvlist[vertex]);
				} // end for vertex

			} // end for poly

		} break;

	default: break;

	} // end switch

	return 0;
} // end Transform


int CRenderList::Model_To_World(CPoint4d& world_pos, 
								int coord_select)
{
	// NOTE: Not matrix based
	// this function converts the local model coordinates of the
	// sent render list into world coordinates, the results are stored
	// in the transformed vertex list (tvlist) within the renderlist

	// interate thru vertex list and transform all the model/local 
	// coords to world coords by translating the vertex list by
	// the amount world_pos and storing the results in tvlist[]
	// is this polygon valid?

	if (coord_select == TRANSFORM_LOCAL_TO_TRANS)
	{
		for (int poly = 0; poly < num_polys; poly++)
		{
			// acquire current polygon
			POLYF4DV1_PTR curr_poly = poly_ptrs[poly];

			// transform this polygon if and only if it's not clipped, not culled,
			// active, and visible, note however the concept of "backface" is 
			// irrelevant in a wire frame engine though
			if ((curr_poly==NULL) || !(curr_poly->state & POLY4DV1_STATE_ACTIVE) ||
				(curr_poly->state & POLY4DV1_STATE_CLIPPED ) ||
				(curr_poly->state & POLY4DV1_STATE_BACKFACE) )
				continue; // move onto next poly

			// all good, let's transform 
			for (int vertex = 0; vertex < 3; vertex++)
			{
				// translate vertex
				curr_poly->tvlist[vertex] = curr_poly->vlist[vertex] + world_pos;
			} // end for vertex

		} // end for poly
	} // end if local
	else // TRANSFORM_TRANS_ONLY
	{
		for (int poly = 0; poly < num_polys; poly++)
		{
			// acquire current polygon
			POLYF4DV1_PTR curr_poly = poly_ptrs[poly];

			// transform this polygon if and only if it's not clipped, not culled,
			// active, and visible, note however the concept of "backface" is 
			// irrelevant in a wire frame engine though
			if ((curr_poly==NULL) || !(curr_poly->state & POLY4DV1_STATE_ACTIVE) ||
				(curr_poly->state & POLY4DV1_STATE_CLIPPED ) ||
				(curr_poly->state & POLY4DV1_STATE_BACKFACE) )
				continue; // move onto next poly

			for (int vertex = 0; vertex < 3; vertex++)
			{
				// translate vertex
				curr_poly->tvlist[vertex] += world_pos;
			} // end for vertex

		} // end for poly

	} // end else

	return 0;

} // end Model_To_World

int CRenderList::World_To_Camera()
{
	// NOTE: this is a matrix based function
	// this function transforms each polygon in the global render list
	// to camera coordinates based on the sent camera transform matrix
	// you would use this function instead of the object based function
	// if you decided earlier in the pipeline to turn each object into 
	// a list of polygons and then add them to the global render list
	// the conversion of an object into polygons probably would have
	// happened after object culling, local transforms, local to world
	// and backface culling, so the minimum number of polygons from
	// each object are in the list, note that the function assumes
	// that at LEAST the local to world transform has been called
	// and the polygon data is in the transformed list tvlist of
	// the POLYF4DV1 object

	// transform each polygon in the render list into camera coordinates
	// assumes the render list has already been transformed to world
	// coordinates and the result is in tvlist[] of each polygon object

	for (int poly = 0; poly < num_polys; poly++)
	{
		// acquire current polygon
		POLYF4DV1_PTR curr_poly = poly_ptrs[poly];

		// is this polygon valid?
		// transform this polygon if and only if it's not clipped, not culled,
		// active, and visible, note however the concept of "backface" is 
		// irrelevant in a wire frame engine though
		if ((curr_poly==NULL) || !(curr_poly->state & POLY4DV1_STATE_ACTIVE) ||
			(curr_poly->state & POLY4DV1_STATE_CLIPPED ) ||
			(curr_poly->state & POLY4DV1_STATE_BACKFACE) )
			continue; // move onto next poly

		// all good, let's transform 
		for (int vertex = 0; vertex < 3; vertex++)
		{
			// transform the vertex by the mcam matrix within the camera
			// it better be valid!
			CPoint4d presult; // hold result of each transformation

			// transform point
			Mat_Mul_VECTOR4D_4X4(curr_poly->tvlist[vertex], m_pCam->m_mcam, presult);

			// store result back
			curr_poly->tvlist[vertex] = presult;
		} // end for vertex

	} // end for poly

	return 0;

} // end World_To_Camera

int CRenderList::Camera_To_Perspective()
{
	// NOTE: this is not a matrix based function
	// this function transforms each polygon in the global render list
	// into perspective coordinates, based on the 
	// sent camera object, 
	// you would use this function instead of the object based function
	// if you decided earlier in the pipeline to turn each object into 
	// a list of polygons and then add them to the global render list

	// transform each polygon in the render list into camera coordinates
	// assumes the render list has already been transformed to world
	// coordinates and the result is in tvlist[] of each polygon object

	for (int poly = 0; poly < num_polys; poly++)
	{
		// acquire current polygon
		POLYF4DV1_PTR curr_poly = poly_ptrs[poly];

		// is this polygon valid?
		// transform this polygon if and only if it's not clipped, not culled,
		// active, and visible, note however the concept of "backface" is 
		// irrelevant in a wire frame engine though
		if ((curr_poly==NULL) || !(curr_poly->state & POLY4DV1_STATE_ACTIVE) ||
			(curr_poly->state & POLY4DV1_STATE_CLIPPED ) ||
			(curr_poly->state & POLY4DV1_STATE_BACKFACE) )
			continue; // move onto next poly

		// all good, let's transform 
		for (int vertex = 0; vertex < 3; vertex++)
		{
			float z = curr_poly->tvlist[vertex][Z];

			// transform the vertex by the view parameters in the camera
			curr_poly->tvlist[vertex].SetX( m_pCam->m_view_dist * curr_poly->tvlist[vertex][X] / z);
			curr_poly->tvlist[vertex].SetY( m_pCam->m_view_dist * curr_poly->tvlist[vertex][Y] * m_pCam->m_aspect_ratio/ z);
			// z = z, so no change

			// not that we are NOT dividing by the homogenous w coordinate since
			// we are not using a matrix operation for this version of the function 

		} // end for vertex

	} // end for poly

	return 0;

} // end Camera_To_Perspective


int CRenderList::Convert_From_Homogeneous4D()
{
	// this function convertes all valid polygons vertices in the transformed
	// vertex list from 4D homogeneous coordinates to normal 3D coordinates
	// by dividing each x,y,z component by w

	for (int poly = 0; poly < num_polys; poly++)
	{
		// acquire current polygon
		POLYF4DV1_PTR curr_poly = poly_ptrs[poly];

		// is this polygon valid?
		// transform this polygon if and only if it's not clipped, not culled,
		// active, and visible, note however the concept of "backface" is 
		// irrelevant in a wire frame engine though
		if ((curr_poly==NULL) || !(curr_poly->state & POLY4DV1_STATE_ACTIVE) ||
			(curr_poly->state & POLY4DV1_STATE_CLIPPED ) ||
			(curr_poly->state & POLY4DV1_STATE_BACKFACE) )
			continue; // move onto next poly

		// all good, let's transform 
		for (int vertex = 0; vertex < 3; vertex++)
		{
			// convert to non-homogenous coords
			curr_poly->tvlist[vertex].DivByW(); 
		} // end for vertex

	} // end for poly

	return 0;

} // end Convert_From_Homogeneous4D

int CRenderList::Perspective_To_Screen()
{
	// NOTE: this is not a matrix based function
	// this function transforms the perspective coordinates of the render
	// list into screen coordinates, based on the sent viewport in the camera
	// assuming that the viewplane coordinates were normalized
	// you would use this function instead of the object based function
	// if you decided earlier in the pipeline to turn each object into 
	// a list of polygons and then add them to the global render list
	// you would only call this function if you previously performed
	// a normalized perspective transform

	// transform each polygon in the render list from perspective to screen 
	// coordinates assumes the render list has already been transformed 
	// to normalized perspective coordinates and the result is in tvlist[]
	for (int poly = 0; poly < num_polys; poly++)
	{
		// acquire current polygon
		POLYF4DV1_PTR curr_poly = poly_ptrs[poly];

		// is this polygon valid?
		// transform this polygon if and only if it's not clipped, not culled,
		// active, and visible, note however the concept of "backface" is 
		// irrelevant in a wire frame engine though
		if ((curr_poly==NULL) || !(curr_poly->state & POLY4DV1_STATE_ACTIVE) ||
			(curr_poly->state & POLY4DV1_STATE_CLIPPED ) ||
			(curr_poly->state & POLY4DV1_STATE_BACKFACE) )
			continue; // move onto next poly

		float alpha = (0.5*m_pCam->m_viewport_width-0.5);
		float beta  = (0.5*m_pCam->m_viewport_height-0.5);

		// all good, let's transform 
		for (int vertex = 0; vertex < 3; vertex++)
		{
			// the vertex is in perspective normalized coords from -1 to 1
			// on each axis, simple scale them and invert y axis and project
			// to screen

			// transform the vertex by the view parameters in the camera
			curr_poly->tvlist[vertex].vector[X] = alpha + alpha*curr_poly->tvlist[vertex][X];
			curr_poly->tvlist[vertex].vector[Y] = beta  - beta *curr_poly->tvlist[vertex][Y];
		} // end for vertex

	} // end for poly

	return 0;
} // end Perspective_To_Screen

int CRenderList::Camera_To_Perspective_Screen()
{
	// NOTE: this is not a matrix based function
	// this function transforms the camera coordinates of an object
	// into Screen scaled perspective coordinates, based on the 
	// sent camera object, that is, view_dist_h and view_dist_v 
	// should be set to cause the desired (viewport_width X viewport_height)
	// it only works on the vertices in the tvlist[] list
	// finally, the function also inverts the y axis, so the coordinates
	// generated from this function ARE screen coordinates and ready for
	// rendering

	// transform each polygon in the render list to perspective screen 
	// coordinates assumes the render list has already been transformed 
	// to camera coordinates and the result is in tvlist[]
	for (int poly = 0; poly < num_polys; poly++)
	{
		// acquire current polygon
		POLYF4DV1_PTR curr_poly = poly_ptrs[poly];

		// is this polygon valid?
		// transform this polygon if and only if it's not clipped, not culled,
		// active, and visible, note however the concept of "backface" is 
		// irrelevant in a wire frame engine though
		if ((curr_poly==NULL) || !(curr_poly->state & POLY4DV1_STATE_ACTIVE) ||
			(curr_poly->state & POLY4DV1_STATE_CLIPPED ) ||
			(curr_poly->state & POLY4DV1_STATE_BACKFACE) )
			continue; // move onto next poly

		float alpha = (0.5*m_pCam->m_viewport_width-0.5);
		float beta  = (0.5*m_pCam->m_viewport_height-0.5);

		// all good, let's transform 
		for (int vertex = 0; vertex < 3; vertex++)
		{
			float z = curr_poly->tvlist[vertex][Z];

			// transform the vertex by the view parameters in the camera
			curr_poly->tvlist[vertex].vector[X] = m_pCam->m_view_dist*curr_poly->tvlist[vertex][X]/z;
			curr_poly->tvlist[vertex].vector[Y] = m_pCam->m_view_dist*curr_poly->tvlist[vertex][Y]/z;
			// z = z, so no change

			// not that we are NOT dividing by the homogenous w coordinate since
			// we are not using a matrix operation for this version of the function 

			// now the coordinates are in the range x:(-viewport_width/2 to viewport_width/2)
			// and y:(-viewport_height/2 to viewport_height/2), thus we need a translation and
			// since the y-axis is inverted, we need to invert y to complete the screen 
			// transform:
			curr_poly->tvlist[vertex].vector[X] =  curr_poly->tvlist[vertex][X] + alpha; 
			curr_poly->tvlist[vertex].vector[Y] = -curr_poly->tvlist[vertex][Y] + beta;

		} // end for vertex

	} // end for poly

	return 0;
} // end Camera_To_Perspective_Screen

int CRenderList::Insert_POLY4DV1(POLY4DV1_PTR poly)
{
	// converts the sent POLY4DV1 into a FACE4DV1 and inserts it
	// into the render list

	// step 0: are we full?
	if (num_polys >= RENDERLIST4DV1_MAX_POLYS)
		return -1;

	// step 1: copy polygon into next opening in polygon render list

	// point pointer to polygon structure
	poly_ptrs[num_polys] = &poly_data[num_polys];

	// copy fields
	poly_data[num_polys].state = poly->state;
	poly_data[num_polys].attr  = poly->attr;
	poly_data[num_polys].color = poly->color;

	// now copy vertices, be careful! later put a loop, but for now
	// know there are 3 vertices always!
	poly_data[num_polys].tvlist[0] = poly->vlist[poly->vert[0]];

	poly_data[num_polys].tvlist[1] = poly->vlist[poly->vert[1]];

	poly_data[num_polys].tvlist[2] = poly->vlist[poly->vert[2]];


	// and copy into local vertices too
	poly_data[num_polys].vlist[0] = poly->vlist[poly->vert[0]];

	poly_data[num_polys].vlist[1] = poly->vlist[poly->vert[1]];

	poly_data[num_polys].vlist[2] = poly->vlist[poly->vert[2]];


	// now the polygon is loaded into the next free array position, but
	// we need to fix up the links

	// test if this is the first entry
	if (num_polys == 0)
	{
		// set pointers to null, could loop them around though to self
		poly_data[0].next = NULL;
		poly_data[0].prev = NULL;
	} // end if
	else
	{
		// first set this node to point to previous node and next node (null)
		poly_data[num_polys].next = NULL;
		poly_data[num_polys].prev = &poly_data[num_polys-1];

		// now set previous node to point to this node
		poly_data[num_polys-1].next = &poly_data[num_polys];
	} // end else

	// increment number of polys in list
	num_polys++;

	// return successful insertion
	return 0;

} // end Insert_POLY4DV1


int CRenderList::Insert_POLYF4DV1(POLYF4DV1_PTR poly)
{
	// inserts the sent polyface POLYF4DV1 into the render list

	// step 0: are we full?
	if (num_polys >= RENDERLIST4DV1_MAX_POLYS)
		return -1;

	// step 1: copy polygon into next opening in polygon render list

	// point pointer to polygon structure
	poly_ptrs[num_polys] = &poly_data[num_polys];

	// copy face right into array, thats it
	memcpy((void *)&poly_data[num_polys],(void *)poly, sizeof(POLYF4DV1));

	// now the polygon is loaded into the next free array position, but
	// we need to fix up the links
	// test if this is the first entry
	if (num_polys == 0)
	{
		// set pointers to null, could loop them around though to self
		poly_data[0].next = NULL;
		poly_data[0].prev = NULL;
	} // end if
	else
	{
		// first set this node to point to previous node and next node (null)
		poly_data[num_polys].next = NULL;
		poly_data[num_polys].prev = 
			&poly_data[num_polys-1];

		// now set previous node to point to this node
		poly_data[num_polys-1].next = 
			&poly_data[num_polys];
	} // end else

	// increment number of polys in list
	num_polys++;

	// return successful insertion
	return 0;

} // end Insert_POLYF4DV1

int CRenderList::Insert_Obj4d(CObj4d& obj, int insert_local, int lighting_on)
{
	// converts the entire object into a face list and then inserts
	// the visible, active, non-clipped, non-culled polygons into
	// the render list, also note the flag insert_local control 
	// whether or not the vlist_local or vlist_trans vertex list
	// is used, thus you can insert an object "raw" totally untranformed
	// if you set insert_local to 1, default is 0, that is you would
	// only insert an object after at least the local to world transform

	unsigned int base_color; // save base color of polygon

	// is this objective inactive or culled or invisible?
	if (!(obj.state & OBJECT4DV1_STATE_ACTIVE) ||
		(obj.state & OBJECT4DV1_STATE_CULLED) ||
		!(obj.state & OBJECT4DV1_STATE_VISIBLE))
		return -1; 

	// the object is valid, let's rip it apart polygon by polygon
	for (int poly = 0; poly < obj.num_polys; poly++)
	{
		// acquire polygon
		POLY4DV1_PTR curr_poly = &obj.plist[poly];

		// first is this polygon even visible?
		if (!(curr_poly->state & POLY4DV1_STATE_ACTIVE) ||
			(curr_poly->state & POLY4DV1_STATE_CLIPPED ) ||
			(curr_poly->state & POLY4DV1_STATE_BACKFACE) )
			continue; // move onto next poly

		// override vertex list polygon refers to
		// the case that you want the local coords used
		// first save old pointer
		CPoint4d* vlist_old = curr_poly->vlist;

		if (insert_local)
			curr_poly->vlist = obj.vlist_local;
		else
			curr_poly->vlist = obj.vlist_trans;

		// test if we should overwrite color with upper 16-bits
		if (lighting_on==1)
		{
			// save color for a sec
			base_color = (unsigned int)(curr_poly->color);
			curr_poly->color = (int)(base_color >> 16);
		} // end if

		// now insert this polygon
		if ( Insert_POLY4DV1(curr_poly) != 0)
		{
			// fix vertex list pointer
			curr_poly->vlist = vlist_old;

			// the whole object didn't fit!
			return -1;
		}

		// test if we should overwrite color with upper 16-bits
		if (lighting_on==1)
		{
			// fix color upc
			curr_poly->color = (int)(base_color & 0xffff);
		} // end if

		// fix vertex list pointer
		curr_poly->vlist = vlist_old;

	} // end for

	// return success
	return 0;

} // end Insert_OBJECT4DV1

int CRenderList::Draw_Wire16()
{
	// this function "executes" the render list or in other words
	// draws all the faces in the list in wire frame 16bit mode
	// note there is no need to sort wire frame polygons, but 
	// later we will need to, so hidden surfaces stay hidden
	// also, we leave it to the function to determine the bitdepth
	// and call the correct rasterizer

	// at this point, all we have is a list of polygons and it's time
	// to draw them
	for (int poly=0; poly < num_polys; poly++)
	{
		// render this polygon if and only if it's not clipped, not culled,
		// active, and visible, note however the concecpt of "backface" is 
		// irrelevant in a wire frame engine though
		if (!(poly_ptrs[poly]->state & POLY4DV1_STATE_ACTIVE) ||
			(poly_ptrs[poly]->state & POLY4DV1_STATE_CLIPPED ) ||
			(poly_ptrs[poly]->state & POLY4DV1_STATE_BACKFACE) )
			continue; // move onto next poly

		// draw the triangle edge one, note that clipping was already set up
		// by 2D initialization, so line clipper will clip all polys out
		// of the 2D screen/window boundary
		m_graphLine.Draw_Line16(poly_ptrs[poly]->tvlist[0][X], 
			poly_ptrs[poly]->tvlist[0][Y],
			poly_ptrs[poly]->tvlist[1][X], 
			poly_ptrs[poly]->tvlist[1][Y],
			poly_ptrs[poly]->color);

		m_graphLine.Draw_Line16(poly_ptrs[poly]->tvlist[1][X], 
			poly_ptrs[poly]->tvlist[1][Y],
			poly_ptrs[poly]->tvlist[2][X], 
			poly_ptrs[poly]->tvlist[2][Y],
			poly_ptrs[poly]->color);

		m_graphLine.Draw_Line16(poly_ptrs[poly]->tvlist[2][X], 
			poly_ptrs[poly]->tvlist[2][Y],
			poly_ptrs[poly]->tvlist[0][X], 
			poly_ptrs[poly]->tvlist[0][Y],
			poly_ptrs[poly]->color);

		// track rendering stats
#ifdef DEBUG_ON
		debug_polys_rendered_per_frame++;
#endif

	} // end for poly

	return 0;

} // end Draw_Wire


int CRenderList::Draw_Solid16()
{
	// this function "executes" the render list or in other words
	// draws all the faces in the list in wire frame 16bit mode
	// note there is no need to sort wire frame polygons, but 
	// later we will need to, so hidden surfaces stay hidden
	// also, we leave it to the function to determine the bitdepth
	// and call the correct rasterizer

	// at this point, all we have is a list of polygons and it's time
	// to draw them
	for (int poly=0; poly < num_polys; poly++)
	{
		// render this polygon if and only if it's not clipped, not culled,
		// active, and visible, note however the concecpt of "backface" is 
		// irrelevant in a wire frame engine though
		if (!(poly_ptrs[poly]->state & POLY4DV1_STATE_ACTIVE) ||
			(poly_ptrs[poly]->state & POLY4DV1_STATE_CLIPPED ) ||
			(poly_ptrs[poly]->state & POLY4DV1_STATE_BACKFACE) )
			continue; // move onto next poly

		// draw the triangle
		m_graphTrig.Draw_Trig_2D16(poly_ptrs[poly]->tvlist[0][X], poly_ptrs[poly]->tvlist[0][Y],
			poly_ptrs[poly]->tvlist[1][X], poly_ptrs[poly]->tvlist[1][Y],
			poly_ptrs[poly]->tvlist[2][X], poly_ptrs[poly]->tvlist[2][Y],
			poly_ptrs[poly]->color);

	} // end for poly

	return 0;
} // end Draw_Solid16

int CRenderList::Remove_Backfaces()
{
	// NOTE: this is not a matrix based function
	// this function removes the backfaces from polygon list
	// the function does this based on the polygon list data
	// tvlist along with the camera position (only)
	// note that only the backface state is set in each polygon

	for (int poly = 0; poly < num_polys; poly++)
	{
		// acquire current polygon
		POLYF4DV1_PTR curr_poly = poly_ptrs[poly];

		// is this polygon valid?
		// test this polygon if and only if it's not clipped, not culled,
		// active, and visible and not 2 sided. Note we test for backface in the event that
		// a previous call might have already determined this, so why work
		// harder!
		if ((curr_poly==NULL) || !(curr_poly->state & POLY4DV1_STATE_ACTIVE) ||
			(curr_poly->state & POLY4DV1_STATE_CLIPPED ) || 
			(curr_poly->attr  & POLY4DV1_ATTR_2SIDED)    ||
			(curr_poly->state & POLY4DV1_STATE_BACKFACE) )
			continue; // move onto next poly

		// we need to compute the normal of this polygon face, and recall
		// that the vertices are in cw order, u = p0->p1, v=p0->p2, n=uxv
		CVector4d u, v, n;

		// build u, v
		u.Build(curr_poly->tvlist[0], curr_poly->tvlist[1]);
		v.Build(curr_poly->tvlist[0], curr_poly->tvlist[2]);


		// compute cross product
		n = u.Cross(v);

		// now create eye vector to viewpoint
		CVector4d view;
		view.Build(curr_poly->tvlist[0], m_pCam->m_pos);

		// and finally, compute the dot product
		float dp = n.Dot(view);

		// if the sign is > 0 then visible, 0 = scathing, < 0 invisible
		if (dp <= 0.0 )
			SET_BIT(curr_poly->state, POLY4DV1_STATE_BACKFACE);

	} // end for poly

	return 0;
} // end Remove_Backfaces


int CRenderList::Sort(int sort_method)
{
	// this function sorts the rendering list based on the polygon z-values 
	// the specific sorting method is controlled by sending in control flags
	// #define SORT_POLYLIST_AVGZ  0 - sorts on average of all vertices
	// #define SORT_POLYLIST_NEARZ 1 - sorts on closest z vertex of each poly
	// #define SORT_POLYLIST_FARZ  2 - sorts on farthest z vertex of each poly

	switch(sort_method)
	{
	case SORT_POLYLIST_AVGZ:  //  - sorts on average of all vertices
		{
			qsort((void *)poly_ptrs, num_polys, sizeof(POLYF4DV1_PTR), Compare_AvgZ_POLYF4DV1);
		} break;

	case SORT_POLYLIST_NEARZ: // - sorts on closest z vertex of each poly
		{
			qsort((void *)poly_ptrs, num_polys, sizeof(POLYF4DV1_PTR), Compare_NearZ_POLYF4DV1);
		} break;

	case SORT_POLYLIST_FARZ:  //  - sorts on farthest z vertex of each poly
		{
			qsort((void *)poly_ptrs, num_polys, sizeof(POLYF4DV1_PTR), Compare_FarZ_POLYF4DV1);
		} break;

	default: break;
	} // end switch

	return 0;

} // end Sort

////////////////////////////////////////////////////////////////////////////////

